/*
 * #%L
 * SwiftUIBusinessLayer
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2011 - 2012 Linkwithweb
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public 
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */
/**
 * 
 */
package com.linkwithweb.products.swiftui.plugins;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.LinkedList;
import java.util.List;

import javax.servlet.ServletContext;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.java.plugin.ObjectFactory;
import org.java.plugin.Plugin;
import org.java.plugin.PluginManager.PluginLocation;
import org.java.plugin.registry.Extension;
import org.java.plugin.registry.ExtensionPoint;
import org.java.plugin.registry.PluginDescriptor;
import org.java.plugin.standard.StandardPluginLocation;
import org.java.plugin.util.ExtendedProperties;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.context.support.XmlWebApplicationContext;

/**
 * @author ashwin kumar
 * 
 */
@Service
public class PluginManager implements InitializingBean,
		ApplicationContextAware, ServletContextAware {
	private static final transient Log logger = LogFactory
			.getLog(PluginManager.class);
	private static final String DEFAULT_PLUGIN = "com.linkwithweb.products.swiftui.plugins.core";
	private static final String DEFAULT_EXTENSION_POINT = "plugin";
	private ApplicationContext applicationContext;
	private ServletContext servletContext;

	private final Resource[] repositories;
	private final org.java.plugin.PluginManager manager;

	
	/**
	 * @param repositories
	 * @throws Exception
	 */
	public PluginManager(org.springframework.core.io.Resource[] repositories)
			throws Exception {
		this.repositories = repositories;

		ExtendedProperties extendedProperties = new ExtendedProperties();
		extendedProperties
				.setProperty("org.java.plugin.ObjectFactory",
						"com.linkwithweb.products.swiftui.plugins.PluginManagerObjectFactory");

		PluginManagerObjectFactory objectFactory = (PluginManagerObjectFactory) ObjectFactory
				.newInstance(extendedProperties);
		objectFactory.setPluginManager(this);
		manager = objectFactory.createManager();
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		PluginLocation[] pluginLocations = discoverPluginLocations();
		manager.publishPlugins(pluginLocations);
		for (PluginDescriptor pd : manager.getRegistry().getPluginDescriptors()) {
			Plugin plugin = manager.getPlugin(pd.getId());
			ExtensionPoint extensionPoint = plugin.getDescriptor()
					.getExtensionPoint(DEFAULT_EXTENSION_POINT);

			for (Extension extension : extensionPoint.getAvailableExtensions()) {
				try {
					PluginDescriptor descriptor = extension
							.getDeclaringPluginDescriptor();
					manager.activatePlugin(descriptor.getId());
				} catch (Exception e) {
					logger.error(e.getMessage(), e);
				}
			}
		}

		if (manager.getRegistry().getPluginDescriptors().size() > 0)
			manager.activatePlugin(DEFAULT_PLUGIN);
	}

	/**
	 * @return
	 * @throws IOException
	 * @throws MalformedURLException
	 */
	private PluginLocation[] discoverPluginLocations() throws IOException,
			MalformedURLException {
		List<PluginLocation> pluginLocations = new LinkedList<PluginLocation>();

		for (Resource repository : repositories) {
			if (!repository.exists())
				continue;

			for (File pluginPath : repository.getFile().listFiles()) {
				/**
				 * Unpack Plugin resources to respective folders /templates to
				 * webinf/classes folder /resources/web to
				 * /plugins/{pluginname}/ /resources/sql to
				 * webinf/sql/{pluginname}/
				 */
				if (StringUtils.containsIgnoreCase(
						pluginPath.getAbsolutePath(), ".svn")) {
					continue;
				}

				unpackPluginResources(pluginPath);

				pluginLocations.add(StandardPluginLocation.create(pluginPath));
			}
		}

		return pluginLocations.toArray(new PluginLocation[pluginLocations
				.size()]);
	}

	/**
	 * Unpack Plugin resources to respective folders /templates to
	 * webinf/classes folder /resources/web to /plugins/{pluginname}/
	 * /resources/sql to webinf/sql/{pluginname}/
	 * 
	 * @param pluginPath
	 */
	private void unpackPluginResources(File pluginPath) {
		int lastIndexOfWebInf = pluginPath.getAbsolutePath().lastIndexOf(
				"WEB-INF");
		String baseFolder = pluginPath.getAbsolutePath().substring(0,
				lastIndexOfWebInf);
		String webInfFolder = pluginPath.getAbsolutePath().substring(0,
				lastIndexOfWebInf)
				+ "WEB-INF\\";
		String classesFolder = pluginPath.getAbsolutePath().substring(0,
				lastIndexOfWebInf)
				+ "WEB-INF\\classes\\";

		String pluginFileName = pluginPath.getName();
		System.out.println("PluginFileName:" + pluginFileName);
		int hyphenIndex = pluginFileName.indexOf("-");
		String pluginName = pluginFileName.substring(0, hyphenIndex);

		pluginName = pluginName.substring(pluginName.lastIndexOf(".") + 1);

		com.linkwithweb.products.swiftui.utils.ZipUtil.extractZip(
				pluginPath.getAbsolutePath(), new String[] { "templates" },
				classesFolder, "templates/");

		com.linkwithweb.products.swiftui.utils.ZipUtil.extractZip(
				pluginPath.getAbsolutePath(),
				new String[] { "resources/webapp" }, baseFolder + "plugins\\"
						+ pluginName, "resources/webapp");

		com.linkwithweb.products.swiftui.utils.ZipUtil.extractZip(
				pluginPath.getAbsolutePath(), new String[] { "resources/sql" },
				classesFolder + "sql\\" + pluginName, "resources/sql");
	}

	/**
	 * @param context
	 */
	public void registerApiPlugin(XmlWebApplicationContext context) {
		context.setParent(applicationContext);
		context.setServletContext(servletContext);
		context.refresh();
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.context.ApplicationContextAware#setApplicationContext
	 *      (org.springframework.context.ApplicationContext)
	 */
	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.applicationContext = applicationContext;

	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see org.springframework.web.context.ServletContextAware#setServletContext
	 *      (javax.servlet.ServletContext)
	 */
	@Override
	public void setServletContext(ServletContext servletContext) {
		this.servletContext = servletContext;

	}

	/**
	 * @param pluginDescriptor
	 * @return
	 */
	public ClassLoader getPluginClassLoader(PluginDescriptor pluginDescriptor) {
		// TODO Auto-generated method stub
		return manager.getPluginClassLoader(pluginDescriptor);
	}
}
